/* 
 * Copyright (c) 2010 Adam Markiewicz
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * @file containers/fixedarray.h
 * @author Adam Markiewicz
 */

#ifndef __CLEA_CONTAINERS_FIXEDARRAY
#define __CLEA_CONTAINERS_FIXEDARRAY

#include <core/types.h>
#include <core/memory/standardallocator.h>
#include <meta/traits.h>
#include <containers/constructpolicy.h>
#include <containers/destroypolicy.h>
#include <containers/copypolicy.h>
#include <containers/iterators.h>

namespace Clea
{
    namespace Containers
    {
        /**
         * @brief Fixed size array
         * 
         * @tparam Type Stored type
         * @tparam Allocator Allocator
         */
        template <typename Type, typename Allocator = Core::Memory::StandardAllocator>
        class FixedArray
        {
            private:
                // Internal types
                typedef typename Clea::Meta::Traits<Type>::value_type value_type;
                typedef typename Clea::Meta::Traits<Type>::pointer pointer;
                typedef typename Clea::Meta::Traits<Type>::const_pointer const_pointer;
                typedef typename Clea::Meta::Traits<Type>::reference reference;
                typedef typename Clea::Meta::Traits<Type>::const_reference const_reference;
                
                typedef typename Clea::Core::size_type size_type;
                typedef typename Clea::Core::difference_type difference_type;
                
                // Used to simplify notation
                typedef FixedArray<Type, Allocator> Container;
                
                // Friend classes to support iterators
                friend class Iterator<Container>;
                friend class ForwardIterator<Container>;
                friend class BackwardIterator<Container>;
                friend class BidirectionalIterator<Container>;
                
            public:
                /**
                 * @brief Constructor
                 * 
                 * Constructs array of size n. If type has a non-default constructor it is called.
                 * Otherwise data remains uninitialized.
                 * 
                 * @param n Size of the array
                 */
                inline FixedArray(const size_type n): 
                    m_begin(static_cast<pointer>(Allocator::allocate(n * sizeof(value_type)))),
                    m_end(m_begin + n)
                {
                    for (pointer i = m_begin; i != m_end; i++) {
                        ConstructPolicy<value_type>::construct(i);
                    }
                }
                
                /**
                 * @brief Constructor
                 * 
                 * Constructs array of size n and initializes all elements with a given value.
                 * 
                 * @param n Size of the array
                 * @param value Value for initialization
                 */
                inline FixedArray(const size_type n, const_reference value):
                    m_begin(static_cast<pointer>(Allocator::allocate(n * sizeof(value_type)))),
                    m_end(m_begin + n)
                {
                    for (pointer i = m_begin; i != m_end; i++) {
                        ConstructPolicy<value_type>::construct(i, value);
                    }
                }
                
                /**
                 * @brief Copy constructor
                 * 
                 * @param array Array to copy
                 */
                inline FixedArray(const FixedArray& array):
                    m_begin(static_cast<pointer>(Allocator::allocate((array.m_end - array.m_begin) * sizeof(value_type)))),
                    m_end(m_begin + (array.m_end - array.m_begin))
                {
                    CopyPolicy<value_type>::copy(m_begin, array.m_begin, m_end - m_begin);
                }
                
                /**
                 * @brief Copy constructor
                 * 
                 * Used to copy array with different allocator
                 * 
                 * @param array Array to copy
                 */
                template <typename SrcAllocator>
                inline FixedArray(const FixedArray<value_type, SrcAllocator>& array):
                    m_begin(static_cast<pointer>(Allocator::allocate((array.m_end - array.m_begin) * sizeof(value_type)))),
                    m_end(m_begin + (array.m_end - array.m_begin))
                {
                    CopyPolicy<value_type>::copy(m_begin, array.m_begin, m_end - m_begin);
                }
                
                /**
                 * @brief Destructor
                 */
                inline ~FixedArray()
                {
                    for (pointer i = m_begin; i != m_end; i++) {
                        DestroyPolicy<value_type>::destroy(i);
                    }
                    
                    Allocator::deallocate(m_begin, m_end - m_begin);
                }
                
                /**
                 * @brief Element at position i
                 * 
                 * Non-const version
                 * 
                 * @param i Position
                 */
                inline reference at(const size_type i) {
                    return m_begin[i];
                }
                
                /**
                 * @brief Element at position i
                 * 
                 * Const version
                 * 
                 * @param i Position
                 */
                inline const_reference at(const size_type i) const {
                    return m_begin[i];
                }
                
                /**
                 * @brief Size of the array
                 */
                inline size_type size() const {
                    return m_end - m_begin;
                }
                
                /**
                 * @brief Access operator
                 * 
                 * Non-const version
                 */
                inline reference operator [](const size_type i) {
                    return m_begin[i];
                }
                
                
                /**
                 * @brief Access operator
                 * 
                 * Const version
                 */
                inline const_reference operator [](const size_type i) const {
                    return m_begin[i];
                }
                
            protected:
                pointer m_begin;    // Beginning of data
                pointer m_end;      // End of data
        };
        
        /**
         * @brief Iterator
         * 
         * Iterator specialization for FixedArray.
         * 
         * @tparam Type Type stored in the array
         * @tparam Allocator Allocator used by the array
         * 
         * @see Iterator
         * @see FixedArray
         */
        template <typename Type, typename Allocator>
        class Iterator<FixedArray<Type, Allocator> >
        {
            private:
                // Internal types
                typedef typename Clea::Meta::Traits<Type>::value_type value_type;
                typedef typename Clea::Meta::Traits<Type>::pointer pointer;
                typedef typename Clea::Meta::Traits<Type>::const_pointer const_pointer;
                typedef typename Clea::Meta::Traits<Type>::reference reference;
                typedef typename Clea::Meta::Traits<Type>::const_reference const_reference;
                
                // Used to simplify syntax
                typedef FixedArray<Type, Allocator> Container;
                
            public:
                /**
                 * @brief Copy constructor
                 * 
                 * @param iter Iterator to copy
                 */
                Iterator(const Iterator& iter): m_ptr(iter.m_ptr), m_array(iter.m_array) {}
                
            protected:
                /**
                 * @brief Constructor
                 * 
                 * Used interally. Required due to inheritance used.
                 */
                Iterator() {}
                
                /**
                 * @brief Constructor
                 * 
                 * Used interally. Required for iterator initialization.
                 */
                Iterator(const pointer ptr, const Container* array) : m_ptr(ptr), m_array(array) {}
                
            public:
                /**
                 * @brief Destructor
                 */
                ~Iterator() {}
                
                /**
                 * @brief Current element
                 * 
                 * Non-const version.
                 */
                reference current() {
                    return *m_ptr;
                }
                
                /**
                 * @brief Current element
                 * 
                 * Const version.
                 */
                const_reference current() const {
                    return m_ptr;
                }
                
                /**
                 * @brief Dereference operator
                 * 
                 * Non-const version.
                 */
                reference operator *() {
                    return *m_ptr;
                }
                
                /**
                 * @brief Dereference operator
                 * 
                 * Const version.
                 */
                const_reference operator *() const {
                    return *m_ptr;
                }
                
                /**
                 * @brief Member access operator
                 * 
                 * Non-const version
                 */
                pointer operator ->() {
                    return m_ptr;
                }
                
                /**
                 * @brief Member access operator
                 * 
                 * Const version
                 */
                const_pointer operator ->() const {
                    return m_ptr;
                }
                
                /**
                 * @brief Equality operator
                 */
                bool operator ==(const Iterator& iter) const {
                    return m_ptr == iter.m_ptr;
                }
                
                /**
                 * @brief Inequality operator
                 */
                bool operator !=(const Iterator& iter) const {
                    return m_ptr != iter.m_ptr;
                }
                
            protected:
                mutable pointer m_ptr;                      // Current element
                const FixedArray<Type, Allocator>* m_array; // Container
        };
        
        /**
         * @brief Forward iterator
         * 
         * Forward iterator specialization for FixedArray.
         * 
         * @tparam Type Type stored in the array
         * @tparam Allocator Allocator used by the array
         * 
         * @see ForwardIterator
         * @see FixedArray
         */
        template <typename Type, typename Allocator>
        class ForwardIterator<FixedArray<Type, Allocator> > : public virtual Iterator<FixedArray<Type, Allocator> >
        {
            private:
                // Internal types
                typedef typename Clea::Meta::Traits<Type>::value_type value_type;
                typedef typename Clea::Meta::Traits<Type>::pointer pointer;
                typedef typename Clea::Meta::Traits<Type>::const_pointer const_pointer;
                typedef typename Clea::Meta::Traits<Type>::reference reference;
                typedef typename Clea::Meta::Traits<Type>::const_reference const_reference;
                
                // Used to simplify syntax
                typedef FixedArray<Type, Allocator> Container;  // Iterated container type
                typedef Iterator<Container> Base;               // Base iterator type
                
            public:
                /**
                 * @brief Constructor
                 * 
                 * Constructs forward iterator pointing to the first element of the array.
                 * 
                 * @param array Array to iterate
                 */
                ForwardIterator(const Container& array): Base(array.m_begin, &array) {}
                
            protected:
                /**
                 * @brief Constructor
                 * 
                 * Used interally. Required due to inheritance used.
                 */
                ForwardIterator() {}
                
                /**
                 * @brief Constructor
                 * 
                 * Used interally. Required for iterator initialization.
                 */
                ForwardIterator(const pointer ptr, const Container* array): Base(ptr, array) {}
                
            public:
                /**
                 * @brief Rewinds iterator to the beginning of the array
                 */
                void begin() {
                    m_ptr = m_array->m_begin;
                }
                
                /**
                 * @brief Advances iterator and returns its value
                 * 
                 * Non-const version.
                 */
                reference next() {
                    m_ptr++;
                    return *m_ptr;
                }
                
                /**
                 * @brief Advances iterator and returns its value
                 * 
                 * Const version.
                 */
                const_reference next() const {
                    m_ptr++;
                    return *m_ptr;
                }
                
                /**
                 * @brief Returns whether iterator is valid
                 */
                bool isValid() const {
                    return m_ptr < m_array->m_end ? true : false;
                }
                
                /**
                 * @brief Advance operator (prefix)
                 */
                ForwardIterator& operator ++() {
                    ++m_ptr;
                    return *this;
                }
                
                /**
                 * @brief Advance operator (postfix)
                 */
                ForwardIterator operator ++(int) {
                    return ForwardIterator(m_ptr++, m_array);
                }
                
                
                operator bool() const {
                    return m_ptr < m_array->m_end ? true : false;
                }
                
            protected:
                // Imported for easy access
                using Base::m_ptr;
                using Base::m_array;
        };
        
        /**
         * Backward iterator specialization for FixedArray.
         * 
         * @tparam Type Type stored in the array
         * @tparam Allocator Allocator used by the array
         * 
         * @see BackwardIterator
         * @see FixedArray
         */
        template <typename Type, typename Allocator>
        class BackwardIterator<FixedArray<Type, Allocator> > : public virtual Iterator<FixedArray<Type, Allocator> >
        {
            private:
                // Internal types
                typedef typename Clea::Meta::Traits<Type>::value_type value_type;
                typedef typename Clea::Meta::Traits<Type>::pointer pointer;
                typedef typename Clea::Meta::Traits<Type>::const_pointer const_pointer;
                typedef typename Clea::Meta::Traits<Type>::reference reference;
                typedef typename Clea::Meta::Traits<Type>::const_reference const_reference;
                
                // Used to simplify syntax
                typedef FixedArray<Type, Allocator> Container;  // Iterated container type
                typedef Iterator<Container> Base;               // Base iterator type
                
            public:
                /**
                 * @brief Constructor
                 * 
                 * Constructs forward iterator pointing to the first element of the array.
                 * 
                 * @param array Array to iterate
                 */
                BackwardIterator(const Container& array): Base(array.m_end - 1, &array) {}
                
            protected:
                /**
                 * @brief Constructor
                 * 
                 * Used interally. Required due to inheritance used.
                 */
                BackwardIterator() {}
                
                /**
                 * @brief Constructor
                 * 
                 * Used interally. Required for iterator initialization.
                 */
                BackwardIterator(const pointer ptr, const Container* array): Base(ptr, array) {}
                
            public:
                /**
                 * @brief Rewinds iterator to the end of the array
                 */
                void end() {
                    m_ptr = m_array->m_end - 1;
                }
                
                /**
                 * @brief Advances iterator and returns its value
                 * 
                 * Non-const version.
                 */
                reference previous() {
                    m_ptr--;
                    return *m_ptr;
                }
                
                /**
                 * @brief Advances iterator and returns its value
                 * 
                 * Const version.
                 */
                const_reference previous() const {
                    m_ptr--;
                    return *m_ptr;
                }
                
                /**
                 * @brief Returns whether iterator is valid
                 */
                bool isValid() const {
                    return m_ptr >= m_array->m_begin ? true : false;
                }
                
                /**
                 * @brief Advance operator (prefix)
                 */
                BackwardIterator& operator --() {
                    --m_ptr;
                    return *this;
                }
                
                /**
                 * @brief Advance operator (postfix)
                 */
                BackwardIterator operator --(int) {
                    return BackwardIterator(m_ptr--, m_array);
                }
                
                /**
                 * @brief Synonym to isValid()
                 */
                operator bool() const {
                    return m_ptr >= m_array->m_begin ? true : false;
                }
                
            protected:
                // Imported for easy access
                using Base::m_ptr;
                using Base::m_array;
        };
        
        /**
         * @brief Bidirectional iterator
         * 
         * Bidirectional iterator specialization for FixedArray
         * 
         * @tparam Type Type stored in the array
         * @tparam Allocator Allocator used by the array
         * 
         * @see BidirectionalIterator
         * @see FixedArray
         */
        template <typename Type, typename Allocator>
        class BidirectionalIterator<FixedArray<Type, Allocator> >:
            public ForwardIterator<FixedArray<Type, Allocator> >,
            public BackwardIterator<FixedArray<Type, Allocator> >
        {
            private:
                // Internal types
                typedef typename Clea::Meta::Traits<Type>::value_type value_type;
                typedef typename Clea::Meta::Traits<Type>::pointer pointer;
                typedef typename Clea::Meta::Traits<Type>::const_pointer const_pointer;
                
                // Used to simplify syntax
                typedef FixedArray<Type, Allocator> Container;  // Iterated container type
                typedef Iterator<Container> Base;               // Base iterator type
                
            public:
                /**
                 * @brief Constructor
                 * 
                 * Constructs bidirectional iterator pointing to the first element of the array.
                 * 
                 * @param array Array to iterate
                 */
                BidirectionalIterator(const Container& array): Base(array.m_begin, &array) {}
            
            protected:
                /**
                 * @brief Constructor
                 * 
                 * Used interally. Required for iterator initialization.
                 */
                BidirectionalIterator(const pointer ptr, const Container* array): Base(ptr, array) {}
                
            public:
                /**
                 * @brief Returns whether iterator is valid
                 */
                bool isVaild() const {
                    return (m_ptr >= m_array->m_begin && m_ptr < m_array->m_end) ? true : false;
                }
                
                /**
                 * @brief Synonym to isValid()
                 */
                operator bool() const {
                    return (m_ptr >= m_array->m_begin && m_ptr < m_array->m_end) ? true : false;
                }
                
            protected:
                // Imported for easy access
                using Base::m_ptr;
                using Base::m_array;
        };
    }
}

#endif // __CLEA_CONTAINERS_FIXEDARRAY
